//-----------------------------------------------------------------------
// <copyright file="DeviceSpecific.cpp" company="Microsoft">
//      Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// Module:
//      DeviceSpecific.cpp
//
// Description:
//      Implements the device specific code for the driver.
//
//      This file contains device specific information, for example, how
//      to send data to the XYZ device as opposed to the ABC device.
//
//      This sample implementation doesn't do anything - it only contains
//      the stubs to be implemented later
//
//-----------------------------------------------------------------------

#include "Common.h"

#include <strsafe.h>

#include <propvarutil.h>

#include "HWIntf.h"

UINT32 GetDeviceParamUInt32(IWDFNamedPropertyStore *pPropStore, LPCWSTR pwszParamName, UINT32 nDefaultValue = 0);
LPWSTR GetDeviceParamString(IWDFNamedPropertyStore *pPropStore, LPCWSTR pwszParamName, LPCWSTR pwszDefault = NULL);
HRESULT SetDeviceParamDWORD(LPCWSTR pwszParamName, DWORD dwValue = 0);

extern bool g_fDeviceIsValid;

static CSideShowHW* g_pSideShow = 0;
static CSideShowButtonEventSink* g_pSideShowEventSink = 0;

static IWDFNamedPropertyStore* s_pPropStore = NULL;

const ULONG DefaultWidth = 320;
const ULONG DefaultHeight = 240;
const ULONG DefaultDepth = 24;

CSideShowHW::~CSideShowHW()
{
}

void CSideShowButtonEventSink::OnButton(CDevice::DeviceEvent buttonEvent)
{
    CDevice::HandleDeviceEvent(buttonEvent);
}

//
// Send a bitmap to your device here.
// pbBitmapData: Packed DIB (BITMAPFILEHEADER + BITMAPINFOHEADER (+ clrtable) + bits)
// cbBitmapData: Length of Packed DIB
//
HRESULT DeviceSpecificDisplayBitmap(const BYTE* pbBitmapData, DWORD cbBitmapData, DWORD dwRenderFlags)
{
    if (!g_pSideShow)
        return E_FAIL;

    return g_pSideShow->DisplayBitmap(pbBitmapData, cbBitmapData, dwRenderFlags);
}

static void CleanUpDeviceObjects()
{
    if (g_pSideShow)
        delete g_pSideShow;

    if (g_pSideShowEventSink)
        delete g_pSideShowEventSink;

    g_pSideShow = 0;
    g_pSideShowEventSink = 0;
}

HRESULT DeviceDisplayInitialization(PCWSTR instance, IWDFNamedPropertyStore *pPropStore)
{
    // Optionally check if the device has already been initialized
    // If it has, don't initialize a second time
    // Make sure to set this variable to true after initializing.
    if (true == g_fDeviceIsValid)
    {
        return E_FAIL;
    }

    if (NULL == s_pPropStore)
    {
        s_pPropStore = pPropStore;
        s_pPropStore->AddRef();
    }

    // Initialize device here.
    if (!g_pSideShowEventSink)
        g_pSideShowEventSink = new(std::nothrow) CSideShowButtonEventSink();

    if (!g_pSideShow)
        g_pSideShow = CreateSideShowHW();

    HRESULT hr;
    if (0 == g_pSideShowEventSink || 0 == g_pSideShow)
    {
        hr = E_OUTOFMEMORY;
        goto ErrorReturn;
    }

    hr = g_pSideShow->Initialize(instance, g_pSideShowEventSink);
    if (S_OK != hr)
        goto ErrorReturn; 

    ULONG width = DefaultWidth;
    ULONG height = DefaultHeight;
    ULONG depth = DefaultDepth;

    g_pSideShow->GetCaps(&width, &height, &depth);

    //
    // Set the device capabilities
    //
    CDevice::SetDeviceCaps(
            L"Windows SideShow DPWS Driver", // wszDeviceFriendlyName
            L"Microsoft",   // wszDeviceManufacturer
            L"1.0.0.0",     // wszDeviceFirmwareVersion
            width,          // dwDeviceHorizontalResolution
            height,         // dwDeviceVerticalResolution
            depth,          // dwDeviceBitDepth
            TRUE);          // bTopDownBitmap

    //
    // Set the renderer capabilities
    //
    WCHAR wszDefaultFontName[100];
    ::LoadString(_Module.GetResourceInstance(), IDS_FONT_NAME, wszDefaultFontName, COUNTOF(wszDefaultFontName));
    
    LPCWSTR pwszFontName = GetDeviceParamString(pPropStore, L"FontName", wszDefaultFontName);  // load font name from device param if exists.

    CRenderer::SetRendererCaps(
            pwszFontName,
            GetDeviceParamUInt32(pPropStore, L"FontSizeInPixels", 14),   // dwFontSizeInPixels
            GetDeviceParamUInt32(pPropStore, L"TitlebarHeight", 27),   // dwTitlebarHeight
            GetDeviceParamUInt32(pPropStore, L"MenuItemHeight", 22), // dwMenuItemHeight
            GetDeviceParamUInt32(pPropStore, L"ScrollAmountInPixels", 22), // dwScrollAmountInPixels
            GetDeviceParamUInt32(pPropStore, L"DefaultThemeIndex", 0)); // dwDefaultThemeIndex

    if (pwszFontName) CoTaskMemFree((LPVOID)pwszFontName);

    return S_OK;

ErrorReturn:
#if DBG
    OutputDebugString(L"DeviceDisplayInitialization() failed.");
#endif
    CleanUpDeviceObjects();
    return hr;
}

HRESULT DeviceButtonsInitialization(void)
{
    return S_OK;
}


HRESULT DeviceDisplayShutdown(void)
{
    // Optionally check if the device has already been shutdown
    // If it has, don't shutdown a second time
    // Make sure to set this variable to false after shutting down.
    if (false == g_fDeviceIsValid)
    {
        return E_FAIL;
    }

    CRenderer::ReleaseRendererCaps();
    CDevice::ReleaseDeviceCaps();
    CleanUpDeviceObjects();
    g_fDeviceIsValid = false;
    s_pPropStore->Release();
    s_pPropStore = NULL;

    return S_OK;
}


HRESULT DeviceButtonsShutdown(void)
{
    return S_OK;
}


HRESULT ConfigureDevice()
{
    // The user picked the "Configure" option from the context menu of the home screen of the device

    // Do configuration work here. For example, open the SideShow Control Panel.

    return S_OK;
}


// Helper functions

UINT32 GetDeviceParamUInt32(IWDFNamedPropertyStore *pPropStore, LPCWSTR pwszParamName, UINT32 nDefaultValue)
{
    PROPVARIANT propVar;
    HRESULT hr;
    UINT32 nRetVal = nDefaultValue;

    PropVariantInit(&propVar);

    hr = pPropStore->GetNamedValue(pwszParamName, &propVar);

    if (SUCCEEDED(hr))
    {
        nRetVal = PropVariantToUInt32WithDefault(propVar, nDefaultValue);

        PropVariantClear(&propVar);
    }

    return nRetVal;
}

HRESULT SetDeviceParamDWORD(LPCWSTR pwszParamName, DWORD dwValue)
{
    PROPVARIANT propVar;
    PropVariantInit(&propVar);
    propVar.vt = VT_UI4;
    propVar.ulVal = dwValue;

    HRESULT hr = s_pPropStore->SetNamedValue(pwszParamName, &propVar);

    PropVariantClear(&propVar);

    return hr;
}


// caller must CoTaskMemFree
LPWSTR GetDeviceParamString(IWDFNamedPropertyStore *pPropStore, LPCWSTR pwszParamName, LPCWSTR pwszDefault)
{
    PROPVARIANT propVar;
    HRESULT hr;
    LPWSTR pwszRetVal = NULL;

    PropVariantInit(&propVar);

    hr = pPropStore->GetNamedValue(pwszParamName, &propVar);

    if (SUCCEEDED(hr))
    {
        PropVariantToStringAlloc(propVar, &pwszRetVal);

        PropVariantClear(&propVar);
    }

    if (!pwszRetVal && pwszDefault)
    {
        size_t cch = wcslen(pwszDefault) + 1;
        pwszRetVal = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));

        if (pwszRetVal)
        {
            StringCchCopy(pwszRetVal, cch, pwszDefault);
        }
    }

    return pwszRetVal;
}
